/*	Rais_CharacterMatrix:	8x5点阵字符形库
	Created:  2020.3.30
	by Raiscies.
	
	characters[][]: the table of characters.
	functions:
	byte uc_index(unsigned char c):					get the index of ultra characters
	byte chrcol(unsigned char chr, byte column)		get a character's column 
	draw_str<I, F>(I inst, F f_set, String str, int16_t x, int16_t y, int16_t end_x, int16_t end_y, int16_t shft_x, int16_t shft_y)
	draw_str<F>(		   F f_set, String str, int16_t x, int16_t y, int16_t end_x, int16_t end_y, int16_t shft_x, int16_t shft_y)
		^ draw string in any matrix screen
		
	paint_str<I, F>(I inst, F f_set, String str, uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t eft_x, uint16_t eft_y)
	paint_str<F>(			F f_set, String str, uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t eft_x, uint16_t eft_y)
		
	
	macro functions:
	colpxl(col, row)								get the pixel state of a column
	pxl(chr, x, y)									get a pixel state of a character
	ARGS_AUTO_MEM_FPTR(instance, function)			return:  instance,&s_extype(instance)::function
													^ this macro function is used as a argument
	ARGS_EXPL_INST_FUNC_T(instance, function)		s_extype(instance), void (s_extype(instance)::*)(uint8_t, uint8_t, bool)
													^ this macro function is used as a template type argument
													
	draw_str_i(instance, function, str, x, y, end_x, end_y, shft_x, shft_y)
		^ this macro function actually is:	draw_str(ARGS_AUTO_MEM_FPTR(instance, function), str, .... )
	paint_str_i(instance, function, str, x, y, width, height, eft_x, eft_y)
		
	drawStr([instance,] f_set, str, x, y, end_x, end_y, shft_x, shft_y)
		^ THIS function is final what we needs: it supports non-member f_set function OR member f_set function!
	paintStr([instance,] f_set, str, x, y, width, height, eft_x, eft_y)
		
*/


#ifndef RAIS_CHARACTER_MATRIX_H
#define RAIS_CHARACTER_MATRIX_H

#include <Arduino.h>
#include <avr/pgmspace.h>

#define CHARACTER_WIDTH 5


//Ultra Characters:
#define uc_block	127	// ■
#define uc_degree	176	// °

//These character shapes are come from a web, but I forgot it...
const byte characters[][CHARACTER_WIDTH] PROGMEM = {
	{0x00, 0x00, 0x00, 0x00, 0x00},// Space: ' ' //ASCII: 32
	{0x00, 0x00, 0x5F, 0x00, 0x00},// !
	{0x00, 0x07, 0x00, 0x07, 0x00},// "
	{0x14, 0x7F, 0x14, 0x7F, 0x14},// #
	{0x24, 0x2A, 0x07, 0x2A, 0x12},// $
	{0x23, 0x13, 0x08, 0x64, 0x62},// %
	{0x37, 0x49, 0x55, 0x22, 0x50},// &
	{0x00, 0x05, 0x03, 0x00, 0x00},// '
	{0x00, 0x1C, 0x22, 0x41, 0x00},// (
	{0x00, 0x41, 0x22, 0x1C, 0x00},// )
	{0x08, 0x2A, 0x1C, 0x2A, 0x08},// *
	{0x08, 0x08, 0x3E, 0x08, 0x08},// +
	{0x00, 0x50, 0x30, 0x00, 0x00},// ,
	{0x08, 0x08, 0x08, 0x08, 0x08},// -
	{0x00, 0x60, 0x60, 0x00, 0x00},// .
	{0x20, 0x10, 0x08, 0x04, 0x02},// /
	{0x3E, 0x51, 0x49, 0x45, 0x3E},// 0
	{0x00, 0x42, 0x7F, 0x40, 0x00},// 1
	{0x42, 0x61, 0x51, 0x49, 0x46},// 2
	{0x21, 0x41, 0x45, 0x4B, 0x31},// 3
	{0x18, 0x14, 0x12, 0x7F, 0x10},// 4
	{0x27, 0x45, 0x45, 0x45, 0x39},// 5
	{0x3C, 0x4A, 0x49, 0x49, 0x30},// 6
	{0x01, 0x71, 0x09, 0x05, 0x03},// 7
	{0x36, 0x49, 0x49, 0x49, 0x36},// 8
	{0x06, 0x49, 0x49, 0x29, 0x1E},// 9
	{0x00, 0x36, 0x36, 0x00, 0x00},// :
	{0x00, 0x56, 0x36, 0x00, 0x00},// ;
	{0x00, 0x08, 0x14, 0x22, 0x41},// <
	{0x14, 0x14, 0x14, 0x14, 0x14},// =
	{0x41, 0x22, 0x14, 0x08, 0x00},// >
	{0x02, 0x01, 0x51, 0x09, 0x06},// ?
	{0x32, 0x49, 0x79, 0x41, 0x3E},// @
	{0x7E, 0x11, 0x11, 0x11, 0x7E},// A
	{0x7F, 0x49, 0x49, 0x49, 0x36},// B
	{0x3E, 0x41, 0x41, 0x41, 0x22},// C
	{0x7F, 0x41, 0x41, 0x22, 0x1C},// D
	{0x7F, 0x49, 0x49, 0x49, 0x41},// E
	{0x7F, 0x09, 0x09, 0x01, 0x01},// F
	{0x3E, 0x41, 0x41, 0x51, 0x32},// G
	{0x7F, 0x08, 0x08, 0x08, 0x7F},// H
	{0x00, 0x41, 0x7F, 0x41, 0x00},// I
	{0x20, 0x40, 0x41, 0x3F, 0x01},// J
	{0x7F, 0x08, 0x14, 0x22, 0x41},// K
	{0x7F, 0x40, 0x40, 0x40, 0x40},// L
	{0x7F, 0x02, 0x04, 0x02, 0x7F},// M
	{0x7F, 0x04, 0x08, 0x10, 0x7F},// N
	{0x3E, 0x41, 0x41, 0x41, 0x3E},// O
	{0x7F, 0x09, 0x09, 0x09, 0x06},// P
	{0x3E, 0x41, 0x51, 0x21, 0x5E},// Q
	{0x7F, 0x09, 0x19, 0x29, 0x46},// R
	{0x46, 0x49, 0x49, 0x49, 0x31},// S
	{0x01, 0x01, 0x7F, 0x01, 0x01},// T
	{0x3F, 0x40, 0x40, 0x40, 0x3F},// U
	{0x1F, 0x20, 0x40, 0x20, 0x1F},// V
	{0x7F, 0x20, 0x18, 0x20, 0x7F},// W
	{0x63, 0x14, 0x08, 0x14, 0x63},// X
	{0x03, 0x04, 0x78, 0x04, 0x03},// Y
	{0x61, 0x51, 0x49, 0x45, 0x43},// Z
	{0x00, 0x00, 0x7F, 0x41, 0x41},// [
	{0x02, 0x04, 0x08, 0x10, 0x20},// '\'
	{0x41, 0x41, 0x7F, 0x00, 0x00},// ]
	{0x04, 0x02, 0x01, 0x02, 0x04},// ^
	{0x40, 0x40, 0x40, 0x40, 0x40},// _
	{0x00, 0x01, 0x02, 0x04, 0x00},// `
	{0x20, 0x54, 0x54, 0x54, 0x78},// a
	{0x7F, 0x48, 0x44, 0x44, 0x38},// b
	{0x38, 0x44, 0x44, 0x44, 0x20},// c
	{0x38, 0x44, 0x44, 0x48, 0x7F},// d
	{0x38, 0x54, 0x54, 0x54, 0x18},// e
	{0x08, 0x7E, 0x09, 0x01, 0x02},// f
	{0x08, 0x14, 0x54, 0x54, 0x3C},// g
	{0x7F, 0x08, 0x04, 0x04, 0x78},// h
	{0x00, 0x44, 0x7D, 0x40, 0x00},// i
	{0x20, 0x40, 0x44, 0x3D, 0x00},// j
	{0x00, 0x7F, 0x10, 0x28, 0x44},// k
	{0x00, 0x41, 0x7F, 0x40, 0x00},// l
	{0x7C, 0x04, 0x18, 0x04, 0x78},// m
	{0x7C, 0x08, 0x04, 0x04, 0x78},// n
	{0x38, 0x44, 0x44, 0x44, 0x38},// o
	{0x7C, 0x14, 0x14, 0x14, 0x08},// p
	{0x08, 0x14, 0x14, 0x18, 0x7C},// q
	{0x7C, 0x08, 0x04, 0x04, 0x08},// r
	{0x48, 0x54, 0x54, 0x54, 0x20},// s
	{0x04, 0x3F, 0x44, 0x40, 0x20},// t
	{0x3C, 0x40, 0x40, 0x20, 0x7C},// u
	{0x1C, 0x20, 0x40, 0x20, 0x1C},// v
	{0x3C, 0x40, 0x30, 0x40, 0x3C},// w
	{0x44, 0x28, 0x10, 0x28, 0x44},// x
	{0x0C, 0x50, 0x50, 0x50, 0x3C},// y
	{0x44, 0x64, 0x54, 0x4C, 0x44},// z
	{0x00, 0x08, 0x36, 0x41, 0x00},// {
	{0x00, 0x00, 0x7F, 0x00, 0x00},// |
	{0x00, 0x41, 0x36, 0x08, 0x00},// }
	{0x02, 0x01, 0x02, 0x04, 0x02},// ~
	//ultra:
	{0xff, 0xff, 0xff, 0xff, 0xff}, //black block: ■		//ASCII: 127	//index 95
	{0x00, 0x40, 0xa0, 0x40, 0x00} 	// °					//unicode:176	//index 96
	
};

byte uc_index(unsigned char c){
	switch(c){
		case uc_degree:	return 96;
		default:		return 95;
	}
}

//return each column's matrix image
inline byte chrcol(unsigned char c, byte column){
	return column > 4 ? 0 : pgm_read_byte(&characters[c < 32 ? 95 : (c > 128 ? uc_index(c) : c - 32)][column]);
}



//reading macro method:
#define colpxl(col, row) (((col) >> (row)) & 1)			//低位在上, 高位在下.
#define pxl(chr, x, y) ((chrcol(chr, x) >> (y)) & 1)
//eg: pxl(uc_degree, 0, 0)       returns: 0



/*	better universal drawing function
	About the function draw_str's arguments:
	point (x, y), (end_x, end_y) is abslute coordinate, (shft_x, shft_y) is relative coordinate
	shft_x: positive is right shift, negative is left shift.
	shft_y: positive is up shift, negative is down shift.
*/

template <typename F>			   
void draw_str(F f_set, String str, int16_t x, int16_t y, int16_t end_x, int16_t end_y, int16_t shft_x, int16_t shft_y){
	if(x > end_x || y > end_y) return;
	uint16_t chr = 0, height = 8;
	uint8_t col = 0; 
	y += shft_y;
	if(shft_x < 0) col = (-shft_x) % 6, chr = (-shft_x) / 6, shft_x = x; else shft_x += x;
	if(shft_y >= 0) height += shft_y, shft_y = 0;
	for(; chr < static_cast<uint16_t>(str.length()); chr++){
		for(; col < 6; col++){
			if(shft_x <= end_x){
				for(uint8_t row = -shft_y; y + row <= end_y && row < height; row++){
					if(pxl(str[chr], col, row)) f_set(shft_x, y + row, true);
				}
				shft_x++;
			}else return;
		}col = 0;
	}
}

template <class I, typename F>
void draw_str(I& inst, F f_set, String str, int16_t x, int16_t y, int16_t end_x, int16_t end_y, int16_t shft_x, int16_t shft_y){
	if(x > end_x || y > end_y) return;
	uint16_t chr = 0, height = 8;
	uint8_t col = 0; 
	y += shft_y;
	if(shft_x < 0) col = (-shft_x) % 6, chr = (-shft_x) / 6, shft_x = x; else shft_x += x;
	if(shft_y >= 0) height += shft_y, shft_y = 0;
	for(; chr < static_cast<uint16_t>(str.length()); chr++){
		for(; col < 6; col++){
			if(shft_x <= end_x){
				for(uint8_t row = -shft_y; y + row <= end_y && row < height; row++){
					if(pxl(str[chr], col, row)) (inst.*f_set)(shft_x, y + row, true);
				}
				shft_x++;
			}else return;
		}col = 0;
	}
}


/*	the function paint_str's arguments:
	f_set:			the function of screen point setter, its arguments should be: (uint16_t x, uint16_t y, bool state)
	str:			the String of being painting.
	x, y:			the starting painting point of painting field, the point shold be the field's top-left-corner.
	width, height:	the width & height of painting field.
	eft_x, eft_y:	the starting painting point of the String, the point should be the String's top-left-corner.
	
	The f_set function will set points from screen(x, y) to screen(x + width, y + height).
	
*/
template <typename F>	//F is the screen point setter function's pointer type
void paint_str(F f_set,String str, uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t eft_x, uint16_t eft_y){
	uint16_t pos_x = 0, col = eft_x % 6;
	height = (height < 8 ? height : 8) - eft_y; height = height > 8 ? 0 : height;
	for(uint16_t chr = eft_x / 6; chr < str.length(); chr++){
		for(; col < 6; col++){
			if(pos_x < width){
				for(uint8_t row = 0; row < height; row++){
					if(pxl(str[chr], col, row + eft_y)) f_set(x + pos_x, y + row, true);
				}
				pos_x++;
			}
		}
		col = 0;
	}
}

template <class I, typename F>	//I is the instance type, F is same as last function.
void paint_str(I& inst, F f_set, String str, uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t eft_x, uint16_t eft_y){
	uint16_t pos_x = 0, col = eft_x % 6;
	height = (height < 8 ? height : 8) - eft_y; height = height > 8 ? 0 : height;
	for(uint16_t chr = eft_x / 6; chr < str.length(); chr++){
		for(; col < 6; col++){
			if(pos_x < width){
				for(uint8_t row = 0; row < height; row++){
					if(pxl(str[chr], col, row + eft_y)) (inst.*f_set)(x + pos_x, y + row, true);
				}
				pos_x++;
			}
		}
		col = 0;
	}
}


/*	call the drawStr / paint_str function example:
	non-member function 'set' :	drawStr(set, str, x, y, end_x, end_y, shft_x, shft_y)
								paintStr(set, str, x, y, width, height, eft_x, eft_y)
	member function	 'pm.set' :	drawStr(pm, set, str, x, y, end_x, end_y, shft_x, shft_y)	
								paintStr(pm, set, str, x, y, width, height, eft_x, eft_y)	
*/

//Come from Rais_Utils.h
template <class T> struct ScreenTypeExtractor		{using type = T;};
template <class T> struct ScreenTypeExtractor<T&>	{using type = T;};
template <class T> struct ScreenTypeExtractor<T*>	{using type = T;};
#define s_extype(obj_or_expr...)	ScreenTypeExtractor<decltype(obj_or_expr)>::type

//Expediently code the class's member function pointer.
//eg: paint_str(ARGS_AUTO_MEM_FPTR(pm, set), str, ...)

//automatically deduce member funtion pointer type:
#define ARGS_AUTO_MEM_FPTR(instance, function)	instance, &s_extype(instance)::function
//explicitly appoint instance type and member function pointer type - for overloaded member function
#define ARGS_EXPL_INST_FUNC_T(instance, function) typename s_extype(instance), void (s_extype(instance)::*)(uint8_t, uint8_t, bool)

//olds:
// #define ARGS_AUTO_MEM_FPTR(instance, function) instance,&decltype(instance)::function

//Expediently cpde the function draw_str(I, F, str, ...) / paint_str(I, F, str, ...)
//eg: paint_str_i(pm, set, str, ...)
#define paint_str_i(instance, function, str, x, y, width, height, eft_x, eft_y) paint_str<ARGS_EXPL_INST_FUNC_T(instance, function)>(ARGS_AUTO_MEM_FPTR(instance, function), str, x, y, width, height, eft_x, eft_y)
#define draw_str_i(instance, function, str, x, y, end_x, end_y, shft_x, shft_y)  draw_str<ARGS_EXPL_INST_FUNC_T(instance, function)>(ARGS_AUTO_MEM_FPTR(instance, function), str, x, y, end_x, end_y, shft_x, shft_y)

//switching functions, for function overloading —— draw_str / paint_str.
#define funcSwitch(_1, _2, _3, _4, _5, _6, _7, _8, _9, func_name, args...) func_name

//the final function we needs:
#define paintStr(args...) funcSwitch(args, paint_str_i, paint_str, paint_str)(args)
#define drawStr(args...) funcSwitch(args, draw_str_i, draw_str, draw_str)(args)


////////////////////////////////////The following macro functions is desperated////////////////////////////////////


//paint a char in any screen
//painting function's params:	f_set(x, y, state)
#define paint_char(f_set, chr, x, y) 									\
	for(byte __col = 0; __col < 5; __col++)								\
		for(byte __row = 0; __row < 8; __row++)							\
			f_set(__col + x, __row + y, pxl(chr, __col, __row))			

//when you want to add some code in the looping block:
#define paint_char_c(f_set, chr, x, y, col_codes, row_codes) do{		\
	for(byte __col = 0; __col < 5; __col++){		col_codes			\
		for(byte __row = 0; __row < 8; __row++){	row_codes			\
			f_set(__col + x, __row + y, pxl(chr, __col, __row)); 	 }}	\
}while(0)

#endif 

